package org.example.handler;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.example.anno.MapperScan;
import org.example.config.Configurations;
import org.example.dto.SqlData;
import org.example.executor.Executor;
import org.example.executor.impl.ExecutorImpl;
import org.example.session.Session;
import org.example.session.SessionInit;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import java.beans.PropertyVetoException;
import java.lang.reflect.*;
import java.util.*;

/**
 * 实现了bean的注册器，同时还实现了session接口，这个接口是自己实现的
 */
@Slf4j
public class RegisterBeanMapperHandler implements ImportBeanDefinitionRegistrar, Session {

    private Configurations configurations; //xml的数据记录

    /**
     *
     * @param importingClassMetadata  添加了import注解的处理器对象
     * @param registry  注册器
     */
    @SneakyThrows   //lombok的异常处理
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //判断是否含有MapperScan注解
        if (!importingClassMetadata.isAnnotated(MapperScan.class.getName())) {
            return;
        }
        //获取mapperscan注解的属性
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName());
        //获取这个mapper包的值
        String value = (String) annotationAttributes.get("value");

        //处理mapper.xml和数据库
        configurations = SessionInit.init();
        log.info("value: {}",value);
        //生成mapper接口的动态代理
        Map<String, Object> map = readMapperPackageData(value);


        if (registry instanceof DefaultListableBeanFactory)
        {
            DefaultListableBeanFactory factory = (DefaultListableBeanFactory) registry;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                //将mapper生成的动态代理添加到bean容器当中，通过名字添加
                factory.registerSingleton(entry.getKey(),entry.getValue());
            }
        }
    }

    /**
     *
     * @param path 包名
     * @return
     * @throws ClassNotFoundException
     * @throws PropertyVetoException
     * @throws DocumentException
     */
    public Map<String,Object> readMapperPackageData(String path) throws ClassNotFoundException, PropertyVetoException, DocumentException {
        Map<String,Object> map = new HashMap<>();
        //reflections的坑：是否excludeObjectClass排除这个包下的，默认为true。
        Reflections reflections = new Reflections(path,new SubTypesScanner(false));
        //如果默认为true，就会报SubType为null，就是说找不到Object相应的数据
        Set<Class<?>> allMapper = reflections.getSubTypesOf(Object.class);
        for (Class<?> aClass : allMapper) {
            //获取动态代理对象
            map.put(aClass.getSimpleName(),getMapper(aClass));
        }
        return map;
    }

    @Override
    public <E> List<E> selectList(SqlData sqlData, Object... params) throws Exception {
        Executor executor = new ExecutorImpl();
        List<Object> query = executor.query(configurations, sqlData, params);
        return (List<E>) query;
    }

    @Override
    public <E> E select(SqlData sqlData, Object... params) throws Exception {
        List<Object> objects = selectList(sqlData, params);
        if (objects == null || objects.size() ==0)
        {
            return null;
        }
        if (objects.size()>1)
        {
            throw new RuntimeException("查到不止一个");
        }

        return (E) objects.get(0);
    }

    @Override
    public <E> E insert(SqlData sqlData, Object... params) throws Exception {
        Executor executor = new ExecutorImpl();
        List<Object> query = executor.query(configurations, sqlData, params);
        return (E) query.get(0);
    }

    @Override
    public <E> E update(SqlData sqlData, Object... params) throws Exception {
        Executor executor = new ExecutorImpl();
        List<Object> query = executor.query(configurations, sqlData, params);
        return (E) query.get(0);
    }

    @Override
    public <E> E delete(SqlData sqlData, Object... params) throws Exception {
        Executor executor = new ExecutorImpl();
        List<Object> query = executor.query(configurations, sqlData, params);
        return (E) query.get(0);
    }

    /**
     *
     * @param mapperType 要生成动态代理的类对象
     * @return
     * @param <T>
     * @throws PropertyVetoException
     * @throws DocumentException
     */
    @Override
    public <T> T getMapper(Class<?> mapperType) throws PropertyVetoException, DocumentException {

        //生成代理，通过代理处理你想要的结果
        Object instance =Proxy.newProxyInstance(mapperType.getClassLoader(), new Class[]{mapperType}, new InvocationHandler() {
            /**
             * method是动态代理对象的某个方法，args是这个方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //获得method的名字和类，拼接之后得到sql的数据，拿到sql的数据去执行相应的逻辑
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                //返回类型
                Type genericReturnType = method.getGenericReturnType();

                //通过这个去configurations拿值
                String key = className+"."+methodName;
                SqlData sqlData = configurations.getMap().get(key);

                //相应方法的sql数据
                String sqlType = sqlData.getType();

                if ("insert".equals(sqlType))
                {
                    return insert(sqlData,args);
                }else if ("update".equals(sqlType))
                {
                    return update(sqlData,args);
                } else if ("delete".equals(sqlType)) {
                    return delete(sqlData,args);
                }

                //是否是参数化类型
                if (genericReturnType instanceof ParameterizedType)
                {
                    return selectList(sqlData,args);
                }else
                {
                    return select(sqlData,args);
                }

            }
        });

        //返回动态代理对象
        return (T) instance;
    }
}
